﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Protocol;
using MQTTnet.Server;
using System.IO;

namespace PlcUI_test
{
    public class JsonCommunication
    {
        public string valueName = "";
        public object value = 0;   // 这个值需要检测是否是值类型，并且不能为空
        public int delayMilliseconds = 0;

        public JsonCommunication(string _valueName, object _value, int _delayMilliseconds)
        {
            valueName = _valueName;
            value = _value;
            delayMilliseconds = _delayMilliseconds;
        }

    }
    public class MqttClientWithJson
    {
        // 公开参数
        /// <summary>
        /// json文件中对应选中焊机的ID键值名称
        /// </summary>
        public static string SelectID_Key = "id";

        private Queue<Queue<KeyValuePair<string, JToken>>> _jsonQueues;
        public Queue<Queue<KeyValuePair<string, JToken>>> JsonQueues
        {
            get
            {
                return _jsonQueues;
            }
            set
            {
                _jsonQueues = value;
            }
        }
        /// <summary>
        /// 使用此事件对类JsonQueues属性进行检查
        /// </summary>
        public event EventHandler JsonQueuesNotEmpty;

        /// <summary>
        /// 基础的类型转换字典，封装好不改，静态字典，直接归属于通信类使用
        /// </summary>
        public static readonly Dictionary<string, Type> TypeMapping = new Dictionary<string, Type>
        {
            { "int", typeof(int) },
            { "float", typeof(float) },
            { "double", typeof(double) },
            { "byte", typeof(byte) },
            { "short", typeof(short) },
            { "ushort", typeof(ushort) },
            { "long", typeof(long) },
            { "ulong", typeof(ulong) },
            { "bool", typeof(bool) },
            { "string", typeof(string) }
        };

        /// <summary>
        /// 配置文件中对应的配置键值
        /// </summary>
        public JObject config = null;  //实例化之后就会有对应的配置

        // 私有参数

        private IMqttClient mqttClient = null;

        private bool mqttIsConnected = false;  // 连接标志
        private string SubscribeTopic = "waam/expected";  // 默认订阅的主题
        private string SubscribePayload = "";   // 发送消息的实例
        private string SubscribeTime = "";
        private string SubscribeQos = "QoS: 0"; // mqtt消息质量

        private MqttClientOptions options = null;

        #region 配置文件读取
        string jsonPath = "Settings\\config.json";
        // 注意这个地方的路径，不能跟linux一样同目录写成"./config.json"

        

        private void ReadConfig()
        {
            // 程序运行目录比当前目录高两级，debug程序在bin\Debug 目录下
            string currentDirectory = Directory.GetCurrentDirectory();
            currentDirectory = Directory.GetParent(currentDirectory).Parent.FullName;
            string configFilePath = Path.Combine(currentDirectory, jsonPath);

            try
            {
                string json = File.ReadAllText(configFilePath);
                config = JObject.Parse(json);
            }
            catch (Exception ex)
            {
                // todo: 处理异常
                Console.WriteLine(ex.Message);
            }
        }
        #endregion

        #region mqtt客户端配置信息
        private string portName = "mqttClient";
        private string ip = "test.mosquitto.org";
        private int port = 1883;
        private int keepAlivePeriod = 60;  // 心跳包间隔时间

        private string userName = "";
        private string passWord = "";
        #endregion

        /// <summary>
        /// 客户端构造器，完成实例化，这部分会占用线程
        /// </summary>
        public MqttClientWithJson()
        {
            JsonQueues = new Queue<Queue<KeyValuePair<string, JToken>>>();   // 初始化队列防止报错反正初始是0
            ReadConfig();  // 读取配置文件
            MQTTConnect();
            ClientSubscribeTopic(SubscribeTopic);
        }

        // 注意，断开连接之后可能还需要重连功能



        /// <summary>
        /// MQTT客户端连接成功事件
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private Task MqttClientConnectedAsync(MqttClientConnectedEventArgs arg)
        {
            try
            {
                mqttClient.SubscribeAsync(SubscribeTopic, MqttQualityOfServiceLevel.ExactlyOnce);  // QoS 2
                Console.WriteLine("MQTT Client Connected");
                mqttIsConnected = true;
                return Task.CompletedTask;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return Task.CompletedTask;
            }
        }

        /// <summary>
        /// MQTT客户端断开连接事件
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private async Task<Task> MqttClientDisconnectedAsync(MqttClientDisconnectedEventArgs arg)
        {
            Console.WriteLine("MQTT Client Disconnected");
            mqttIsConnected = false;  // 用这个标志位来表示是否完成
            await ScheduleReconnect();
            return Task.CompletedTask;
        }

        // 一直重连
        private async Task ScheduleReconnect()
        {
            await Task.Delay(1000);  // 等待1秒
            try
            {
                MQTTConnect();  // 重新连接
            }
            catch (Exception)
            {
                await ScheduleReconnect();  // 递归调用，直到连接成功
            }
        }


        /// <summary>
        /// MQTT客户端订阅主题
        /// </summary>
        /// <param name="topic"></param>
        private async void ClientSubscribeTopic(string topic)
        {
            if (mqttIsConnected)
            {
                await mqttClient.SubscribeAsync(topic);   // 调用订阅的函数
            }
        }

        /// <summary>
        /// MQTT客户端接收事件并处理
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private Task MqttClientApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
        {
            byte[] data = arg.ApplicationMessage.PayloadSegment.ToArray();  // 通讯协议拿到的数组
            SubscribeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
            SubscribeTopic = "Topic: " + arg.ApplicationMessage.Topic;
            switch (arg.ApplicationMessage.QualityOfServiceLevel)
            {
                case MqttQualityOfServiceLevel.AtMostOnce:
                default:
                    {
                        SubscribeQos = "QoS: 0";
                        break;
                    }
                case MqttQualityOfServiceLevel.AtLeastOnce:
                    {
                        SubscribeQos = "QoS: 1";
                        break;
                    }
                case MqttQualityOfServiceLevel.ExactlyOnce:
                    {
                        SubscribeQos = "QoS: 2";
                        break;
                    }
            }
            SubscribePayload = Encoding.UTF8.GetString(data);   // 根据UTF-8解码转换，形成对应的数据

            // StringJson2Queue(SubscribePayload);
            if(JsonQueues!=null)
                JsonQueues.Enqueue(MQTTJson2Array(SubscribePayload));
            if (JsonQueues.Count > 0)
            {
                JsonQueuesNotEmpty?.Invoke(this, EventArgs.Empty);  //触发该事件，处理数据
            }
            return Task.CompletedTask;
        }

        /// <summary>
        /// 连接
        /// </summary>
        private  void MQTTConnect()
        {
            try
            {
                mqttIsConnected = false;
                var mqttFactory = new MqttFactory();

                var mqttClientOptions = new MqttClientOptionsBuilder()
                    .WithTcpServer(ip, port) // 连接的ip和端口
                    .WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311) // 协议版本
                    .WithClientId(portName)  // 唯一标志号
                    .WithCleanSession(false)
                    // 设置是否使用清洁会话。
                    // false表示会话状态（如订阅和未确认的消息）在客户端断开连接后会被保留。
                    // true表示每次连接时都会创建一个新的会话。
                    .WithCredentials(userName, passWord)
                    .WithKeepAlivePeriod(TimeSpan.FromSeconds(keepAlivePeriod))
                    .Build();
                mqttClient = mqttFactory.CreateMqttClient();
                mqttClient.DisconnectedAsync += MqttClientDisconnectedAsync;
                mqttClient.ConnectedAsync += MqttClientConnectedAsync;
                mqttClient.ApplicationMessageReceivedAsync += MqttClientApplicationMessageReceivedAsync;
                // 这三句分别给三个事件增加事件处理方法
                // 断联之后的处理
                // 连接之后的处理
                // 收到消息的处理
                options = mqttClientOptions;   // 用变量保存mqttClientOptions
                Task task = mqttClient.ConnectAsync(options);
                // 这一部分才将mqttClient连接到服务器
                // 将设置好的mqttClientOptions这个类传递给ConnectAsync函数
                task.Wait();
                task = mqttClient.SubscribeAsync(SubscribeTopic);  // 直接订阅默认的主题
                task.Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "MQTT连接出错");
            }
        }

        /// <summary>
        /// 断联
        /// </summary>
        private async void MQTTDisconnect()
        {
            if (mqttIsConnected)
            {
                try
                {
                    await mqttClient.DisconnectAsync();
                    // 这个就是断开事件，比较简单，主要是连接的配置麻烦
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message + "MQTT断开连接出错");
                }
            }
        }

        /// <summary>
        /// 订阅topic
        /// </summary>
        private void MQTTSubscribe()
        {
            Console.WriteLine("请输入订阅主题topic");
            string topic = Console.ReadLine();
            if (!string.IsNullOrWhiteSpace(topic))
            {
                ClientSubscribeTopic(topic);
            }
            else
            {
                Console.WriteLine("请输入Topic");
            }
        }

        /// <summary>
        /// 发布消息
        /// </summary>
        private void ClientPublishTopic()
        {
            Console.WriteLine("需要写入的内容");
            string payload = Console.ReadLine();
            Console.WriteLine("需要写入的topic");
            string mqttTopic = Console.ReadLine();
            if (!string.IsNullOrWhiteSpace(payload))
            {
                var applicationMessage = new MqttApplicationMessageBuilder()
                    .WithTopic(mqttTopic)
                    .WithPayload(payload)
                    .Build();
                Task<MqttClientPublishResult> task = mqttClient.PublishAsync(applicationMessage);
                task.Wait();
            }
        }


        /// <summary>
        ///  将字符串转换为JsonCommunication队列，预定操作流程使用
        /// </summary>
        /// <param name="inputString"></param>
        /// <returns></returns>
        public Queue<JsonCommunication> StringJson2Queue(string inputString)
        {
            Queue<JsonCommunication> jsonQueue = new Queue<JsonCommunication>();
            // 具体实现



            return jsonQueue;
        }

        /// <summary>
        /// 将接收到的数据转换成Json，然后加载到队列中
        /// </summary>
        /// <param name="jsonObject"></param>
        private void QueueAndNewJObject(Queue<KeyValuePair<string, JToken>> queue, JObject jsonObject)
        {
            foreach (var item in jsonObject.Properties())
            {
                queue.Enqueue(new KeyValuePair<string, JToken>(item.Name, item.Value));
            }
        }

        /// <summary>
        /// 将字符串转成对应的jsonobject或者jsonArray，返回一整段的"dutyRatio":30, 一个消息生成一个队列
        /// </summary>
        /// <param name="jsonString"></param>
        /// <returns name = "Queue<KeyValuePair<string, JToken>的队列"></returns>
        private Queue<KeyValuePair<string, JToken>> MQTTJson2Array(string jsonString)
        {
            // 每次处理这种字符串数据=，一个队列处理一个消息
            Queue<KeyValuePair<string, JToken>> queue = new Queue<KeyValuePair<string, JToken>>();
            try
            {
                JArray jsonArray = JArray.Parse(jsonString);
                foreach (JObject item in jsonArray)
                {
                    QueueAndNewJObject(queue, item);  // 这里处理一行数据 "dutyRatio":30,
                }
                return queue;
            }
            catch (Newtonsoft.Json.JsonReaderException ex)
            {
                try
                {
                    JObject jsonObject = JObject.Parse(jsonString);
                    QueueAndNewJObject(queue, jsonObject);
                }
                catch
                {
                    Console.WriteLine("JSON格式错误");
                }
                return queue;
            }
        }



        //[{
        //"ID":1,
        //"dutyRatio":30,
        //"baseCurrent":10,
        //"peakCurrent":200,
        //"wireFeedSpeed":5,
        //"pulseFrequency":5,
        //"wireHeatCurrent":200
        //}]

        /// <summary>
        /// 将json字符串转换为Queue<KeyValuePair<string, object>>队列，初始化设置
        /// </summary>
        /// <param name="inputString"></param>
        /// <returns></returns>
        public Queue<KeyValuePair<string, object>> StringJson2QueueInitialize(string inputString)
        {
            Queue<KeyValuePair<string, object>> jsonQueue = new Queue<KeyValuePair<string, object>>();
            // 具体实现
            JObject jsonObject = JObject.Parse(inputString);
            Queue<KeyValuePair<string, object>> queue = new Queue<KeyValuePair<string, object>>();
            foreach (var item in jsonObject.Properties())
            {
                queue.Enqueue(new KeyValuePair<string, object>(item.Name, item.Value));
            }
            return jsonQueue;
        }
    }




}
